﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;

namespace JH
{
    public static class InstantiateInheritedClass
    {
        /// <summary>
        /// 类型缓存字典，用于存储已经扫描过的类型
        /// </summary>
        private static readonly Dictionary<Type, HashSet<Type>> TypeCache = new Dictionary<Type, HashSet<Type>>();

        /// <summary>
        /// 利用反射找到所有能实例化的子类，然后挨个实例化，返回实例化后的列表
        /// </summary>
        /// <typeparam name="T">基类或接口类型</typeparam>
        /// <param name="ctorParams">构造函数参数</param>
        /// <param name="filter">可选的类型过滤器</param>
        /// <param name="throwOnLoadAssemblyError">是否抛出加载程序集异常</param>
        /// <returns>实例化后的对象集合</returns>
        public static HashSet<T> GetSubInstanceList<T>(object[] ctorParams = null, Func<Type, bool> filter = null
            , bool throwOnLoadAssemblyError = true)
        {
            // 获取基类型
            var baseType = typeof(T);

            // 创建结果集合
            var instances = new HashSet<T>();

            try
            {
                // 获取所有符合条件的子类型
                var subTypes = GetSubTypes(baseType, filter, throwOnLoadAssemblyError);

                // 遍历所有子类型
                foreach (var type in subTypes)
                {
                    // 获取构造函数
                    var constructor =
                        type.GetConstructor(ctorParams?.Select(p => p.GetType()).ToArray() ?? Type.EmptyTypes);
                    if (constructor == null) continue;

                    try
                    {
                        // 调用构造函数创建实例
                        var instance = (T)constructor.Invoke(ctorParams ?? Array.Empty<object>());
                        if (instance != null)
                        {
                            instances.Add(instance);
                        }
                    }
                    catch (Exception ex)
                    {
                        // 记录实例化过程中的错误，但继续处理其他类型
                        Debug.LogError($"实例化类型 {type.FullName} 时发生错误: {ex.Message}");
                        throw;
                    }
                }
            }
            catch (Exception ex)
            {
                // 记录整个过程中的任何未预期的错误
                Debug.LogError($"在获取子类实例列表时发生错误: {ex.Message}");
            }

            // 返回实例化后的对象集合
            return instances;
        }

        /// <summary>
        /// 获取指定类型的所有子类型
        /// </summary>
        private static HashSet<Type> GetSubTypes(Type baseType, Func<Type, bool> filter,
            bool throwOnLoadAssemblyError = true)
        {
            // 如果缓存中存在该类型的子类型，直接返回缓存的结果
            if (TypeCache.TryGetValue(baseType, out var cachedTypes))
            {
                return new HashSet<Type>(cachedTypes.Where(t => filter?.Invoke(t) ?? true));
            }

            var subTypes = new HashSet<Type>();

            // 获取当前应用程序域中的所有程序集
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

            // 遍历所有程序集
            foreach (var assembly in assemblies)
            {
                Type[] types;
                try
                {
                    // 获取程序集中的所有类型
                    types = assembly.GetTypes();
                }
                catch (ReflectionTypeLoadException)
                {
                    // 如果无法加载某些类型，继续处理下一个程序集
                    if (!throwOnLoadAssemblyError) continue;

                    Debug.LogError($"无法加载程序集 {assembly.FullName} 中的某些类型。");
                    throw;
                }

                // 遍历程序集中的所有类型
                foreach (var type in types)
                {
                    // 检查类型是否满足条件：不是抽象类，不是基类本身，是基类的子类
                    if (!type.IsClass || type.IsAbstract || type == baseType || !baseType.IsAssignableFrom(type))
                        continue;

                    subTypes.Add(type);
                }
            }

            //将结果存入类型缓存
            TypeCache[baseType] = subTypes;

            // 应用过滤器（如果有）并返回结果
            return new HashSet<Type>(subTypes.Where(t => filter?.Invoke(t) ?? true));
        }
    }
}